/*
 * SpeedoDyno - Speedometer and dynamometer application for Android 
 * Copyright 2013 Guilhem VALENTIN
 * 
 * This file is part of SpeedoDyno
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with SpeedoDyno. If not, see <http://www.gnu.org/licenses/>.
 */
package com.gvalentin.speedodyno.data;

import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;

import android.location.Location;
import android.util.Log;

/**
 * Calculate incline by getting altitude from Internet
 * 
 * @author guilhem
 */
public class CalculatorInclineInternet implements Calculator<Double> {

	private static final String TAG = CalculatorInclineInternet.class.getSimpleName();
	
	/** Minimum time between 2 requests to web service */
	private static final long MIN_TIME = 5000; // milliseconds
	
	/** Minimum distance between 2 requests to web service */
	private static final double MIN_DISTANCE = 50.0d; // meters

	
	/** Last position when incline was calculated */
	private Location previousLocation = null;
	
	/** Calculated incline */
	private double previousAltitude = 0;
	
	/** Calculated incline */
	private double incline;
	
	/**
	 * Return the calculated incline
	 * @see com.gvalentin.speedodyno.data.Calculator#getData()
	 */
	@Override
	public Double getData() {
		return incline;
	}
	
	/**
	 * Calculate the incline
	 * @see com.gvalentin.speedodyno.data.Calculator#calculate(android.location.Location, android.location.Location)
	 */
	@Override
	public void update(Location previous, Location current) {
		if (current != null && previous != null) {
			// Init variables for "previous" data
			if (previousLocation == null) {
				previousLocation = previous;
				previousAltitude = previous.getAltitude();
			}

			// Check limit to avoid calling web service too often
			long elapsed = current.getTime() - previousLocation.getTime();
			double distance = current.distanceTo(previousLocation);
			if (elapsed > MIN_TIME && distance > MIN_DISTANCE) {
				// Calculate altitude of symetric point
				double latitude = (previousLocation.getLatitude() + current.getLatitude()) / 2;
				double longitude = (previousLocation.getLongitude() + current.getLongitude()) / 2;
				double altitude = getElevationFromGoogleAPI(latitude, longitude);
				
				// Calculate new incline
				incline = (altitude - previousAltitude) / distance;
				
				// Store "previous" data
				previousLocation = current;
				previousAltitude = altitude;
			}
		}
	}
	
	/**
	 * Get last retrieved altitude
	 */
	double getPreviousAltitude() {
		return previousAltitude;
	}
	
	/**
	 * Get altitude from Google Elevation API
	 * 
	 * Usage Limits: Use of the Google Geocoding API is subject to a query limit
	 * of 2,500 geolocation requests per day. (User of Google Maps API Premier
	 * may perform up to 100,000 requests per day.) This limit is enforced to
	 * prevent abuse and/or repurposing of the Geocoding API, and this limit
	 * may be changed in the future without notice. Additionally, we enforce a
	 * request rate limit to prevent abuse of the service. If you exceed the
	 * 24-hour limit or otherwise abuse the service, the Geocoding API may stop
	 * working for you temporarily. If you continue to exceed this limit, your
	 * access to the Geocoding API may be blocked. Note: the Geocoding API may
	 * only be used in conjunction with a Google map; geocoding results without
	 * displaying them on a map is prohibited. For complete details on allowed
	 * usage, consult the Maps API Terms of Service License Restrictions.
	 */
	double getElevationFromGoogleAPI(double latitude, double longitude) {
        double result = Double.NaN;
        HttpClient httpClient = new DefaultHttpClient();
        HttpContext localContext = new BasicHttpContext();
        String url = "http://maps.googleapis.com/maps/api/elevation/"
                + "xml?locations=" + String.valueOf(latitude)
                + "," + String.valueOf(longitude)
                + "&sensor=true";
		Log.d(TAG, "Getting altitude from Google API: "+url);
        HttpGet httpGet = new HttpGet(url);
        try {
            HttpResponse response = httpClient.execute(httpGet, localContext);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                InputStream instream = entity.getContent();
                int r = -1;
                StringBuffer respStr = new StringBuffer();
                while ((r = instream.read()) != -1)
                    respStr.append((char) r);
                String tagOpen = "<elevation>";
                String tagClose = "</elevation>";
                if (respStr.indexOf(tagOpen) != -1) {
                    int start = respStr.indexOf(tagOpen) + tagOpen.length();
                    int end = respStr.indexOf(tagClose);
                    String value = respStr.substring(start, end);
                    result = (double)(Double.parseDouble(value));
                    
            		Log.d(TAG, "Retrieved elevation: "+result);
                }
                instream.close();
            }
        } catch (ClientProtocolException e) {} 
        catch (IOException e) {}

        return result;
    }
	
	/*
	 * Other possible source for altitude:
	 * http://open.mapquestapi.com/elevation/v1/profile?callback=handleHelloWorldResponse&shapeFormat=raw&latLngCollection=43.5795450210571,1.29047513008117&unit=k&outShapeFormat=none&outFormat=xml
	 */
}
